// 数据的劫持
function observe(data) {
  if (typeof data != 'object' || data == null) return
  if (Array.isArray(data)) {
    // data.__proto__
  } else {
    // 只循环第1层,如果有多个层级，就需要递归了
    Object.keys(data).forEach((key) => {
      reactive(data, key, data[key])
    })
  }
}
function reactive(data, key, value) {
  // 递归完成数据的监听  此处就是vue中的性能较低的原因之一
  // 优化的一原则：data中定义的数据尽可能的扁平化
  observe(value)
  let dep = new Dep()
  Object.defineProperty(data, key, {
    get() {
      // console.log('get', key)
      Dep.target && dep.add(Dep.target)
      return value
    },
    set(v) {
      if (v != value) {
        console.log('set', key)
        // 修改源数据
        value = v
        // 通过视图更新
        document.querySelector('[v-html]').innerHTML = v
        dep.notify()
      }
    }
  })
}

function proxyData(vue) {
  Object.keys(vue.$data).forEach((key) => {
    Object.defineProperty(vue, key, {
      get() {
        return vue.$data[key]
      },
      set(v) {
        if (v != vue.$data[key]) {
          vue.$data[key] = v
        }
      }
    })
  })
}

class Vue {
  constructor(options) {
    // 得到挂载点
    this.$el = document.querySelector(options.el)
    this.$data = options.data

    // 数据的劫持
    observe(options.data)
    // 代理简化获取数据
    proxyData(this)

    // 模板编译
    new Compile(this.$el, options.data)
  }
}

class Compile {
  constructor(rootDom, data) {
    this.compileTemplate(rootDom, data)
  }

  compileTemplate(rootDom, data) {
    // 查询根节点下面的子节点
    let childs = rootDom.childNodes
    // 循环遍历子元素
    childs.forEach((node) => {
      if (node.nodeType == 1) {
        // 元素节点
        this.compileElement(node, data)
        // 元素节点它还有可能有子节点
        if (node.childNodes.length > 0) this.compileTemplate(node, data)
      } else if (node.nodeType == 3 && /\{\{\s*(\w+)\s*\}\}/.test(node.nodeValue)) {
        // 文本节点
        this.commpileText(node, data, RegExp.$1)
      }
    })
  }

  // 元素节点的处理
  compileElement(node, data) {
    // 获取当前dom中所有的属性
    // 它是一个map对象,转换成为数组，方便迭代
    const attrs = [...node.attributes]
    if (attrs.length > 0) {
      attrs.forEach(({ name, value }) => {
        // 只要v-开头的属性，它才是我们要去绑定或事件处理的属性  指令
        if (name.startsWith('v-')) {
          this[name.slice(2)](node, data, value)
        }
      })
    }
  }

  // 文本节点处理
  commpileText(node, data, key) {
    node.textContent = data[key]
    new Watcher(data, key, (val) => (node.textContent = val))
  }

  html(node, data, key) {
    node.innerHTML = data[key]
    // 观察 innerHTML操作的变化
    new Watcher(data, key, (val) => (node.innerHTML = val))
  }
  text(node, data, key) {
    node.textContent = data[key]
    new Watcher(data, key, (val) => (node.textContent = val))
  }
  // v-model它就是一个语法糖，它就是 value和oninput事件的简写
  model(node, data, key) {
    // 输入事件
    node.addEventListener('input', (e) => {
      data[key] = e.target.value
      node.value = e.target.value
    })
  }
}

// 数据的更新视图操作，如果发布订阅模式完成 统一
// 观察者
class Watcher {
  constructor(data, key, updater) {
    Dep.target = this
    this.$data = data
    this.$key = key
    this.updater = updater
    data[key]
    Dep.target = null
  }

  update() {
    // 更新操作 diff就是在这里完成
    this.updater(this.$data[this.$key])
  }
}

// 收集观察依赖类
class Dep {
  // 所有观察对象
  deps = []

  add(watcher) {
    this.deps.push(watcher)
  }

  notify() {
    this.deps.forEach((watcher) => watcher.update())
  }
}
